                         ============================
                         PSID FILE FORMAT DESCRIPTION
                         ============================

                                    v2NG

AUTHORS:
Michael Schwendt <sidplay@geocities.com> (PSID v1 and v2)
Simon White <sidplay2@yahoo.com> (PSID v2NG)
Dag Lem <dag@nimrod.no>(PSID v2NG)
LaLa <LaLa@C64.org> (This document)


INTRODUCTION
============

This document describes the PSID v2NG file format used for SID tunes
in the HVSC (High Voltage SID Collection - http://www.hvsc.c64.org).
It is based mostly on Michael Schwendt's excellent document found at:

http://www.geocities.com/SiliconValley/Lakes/5147/sidplay/doc_formats.html

and the PSID v2NG extensions described by Simon White and Dag Lem in:

http://sidplay2.sourceforge.net

PSID v2NG files use the .sid file extension.

Since PSID v2 is simply an extension of PSID v1 and PSID v2NG is an
extension of PSID v2, all of the formats are discussed below.

The information presented here targets programmers, freaks, or other people
with reasonable background. It is not suitable for newbies who have never
before used a machine code monitor, a disassembler, or a hexadecimal editor.


The PSID file header v1
=======================

The detailed structure of the PSID header looks like the following. Header
offsets are in hexadecimal notation. Other integer values are decimal unless
explicitly marked otherwise. Any stored integer values are in big-endian
format:

+00    ``PSID''

This is a four byte long ASCII character string containing the value
0x50534944.

+04    WORD version

Available version number can either be 0001 or 0002. Headers of version 2
provide additional fields.

+06    WORD dataOffset

This is the offset from the start of the file to the C64 binary data area.
Because of the fixed size of the header, this is either 0x0076 for version 1
and 0x007C for version 2. Whether the value in this field might also be the
address of the used Amiga timer interrupt handler is unconfirmed. Although
interrupt level 7 really is at address 0x0000007C, the other value would be
invalid.

+08    WORD loadAddress

The C64 memory location where to put the C64 data. 0 means the data are in
original C64 binary file format, i.e. the first two bytes contain the
little-endian load address (low byte, high byte).

You must be absolutely sure what to enter here. There is no way to detect
automatically whether the first two bytes in a C64 data file are meant to be a
load address or some arbitrary bytes of code or data. Unless your C64 file is
not a normal binary file and thus has no load address in front, you need not
enter anything else than 0 here. The sidtune will not play if you specify a
load address which is present in the C64 file already.

Normal C64 binary data files have a load address in their first two bytes, so
they can be loaded to a pre-defined destination address by executing
LOAD"FILE",8,1, for instance. If a load address is explicitly specified in the
sidtune info file, some sidtune converters and utilities conjecture that the
C64 data don't have a load address in their first two bytes. Hence, the
explicit load address from the info file is moved in front of the C64 data to
create a valid C64 binary file which can be easily loaded on a C64, too. If
that C64 file were to be saved, it would contain two superfluous data bytes at
offset 2 if an original load address had been in the first two bytes of the
old file. This process of adding a duplicate load address can be repeated. The
file loader strips off the first two bytes (the used load address) and puts
the rest of the file contents (including the now obsolete load address at file
offset 2) into memory. If the new load address is the same than the old one
the two added bytes cause the whole data to be displaced by two bytes, which
most likely results in malfunctioning code. Also, superfluous bytes in memory
then can confuse disassemblers which start at the beginning of the file or
memory buffer.

+0A    WORD initAddress

The start address of the machine code subroutine that initializes a song,
accepting the contents of the 8-bit 6510 Accumulator as the song number
parameter. 0 means the address is equal to the effective load address.

+0C    WORD playAddress

The start address of the machine code subroutine that can be called frequently
to produce a continuous sound. 0 means the initialization subroutine is
expected to install an interrupt handler, which then calls the music player at
some place.

+0E    WORD songs

The number of songs (or sound effects) that can be initialized by calling the
init address. The minimum is 1. The maximum is 256.

+10    WORD startSong

The song number to be played by default. This value is meant as a proposal and
is optional. It often specifies the first song you would hear upon starting
the program is has been taken from. It has a default of 1.

+12    LONGWORD speed

This is a 32 bit big endian number. Each bit in 'speed' specifies the speed
for the corresponding tune number, i.e. bit 0 specifies the speed for tune 1.
If there are more than 32 tunes, the speed specified for tune 32 is also used
for all higher numbered tunes.

A 0 bit specifies vertical blank interrupt (50Hz PAL, 60Hz NTSC), and a 1 bit
specifies CIA 1 timer interrupt (default 60Hz).

Surplus bits in 'speed' should be set to 0.

Note that if 'play' = 0, the bits in 'speed' should still be set for backwards
compatibility with older SID players. New SID players running in a C64
environment will ignore the speed bits in this case.

WARNING: This field does not work in PlaySID for Amiga like it was intended,
therefore the above is a redefinition of the original 'speed' field in PSID
v2NG! See also the 'clock' (video standard) field described below for 'flags'.

+16    ``<name>''
+36    ``<author>''
+56    ``<copyright>''

These are 32 byte long zero terminated ASCII character strings. Upon
evaluating the header, a zero byte will always be put into the last byte of
each string. So the maximum number of available free characters is 31.

+76    <data>

Version 1 of the PSID header is complete at this point. The binary C64 data
starts here.


The PSID file header v2
=======================

Version 2 of the header provides additional fields. Some of these are actually
v2NG specific - those are noted below.

+76    WORD flags

This is a 16 bit big endian number containing the following bitfields:

- Bit 0 specifies format of the binary data (musPlayer):
  0 = built-in music player,
  1 = Compute!'s Sidplayer MUS data, music player must be merged.

  If this bit is set, the appended binary data are in Compute!'s Sidplayer
MUS format, and does not contain a built-in music player. An external player
machine code must be merged to replay such a sidtune. This field almost ever
contains a zero.

- Bit 1 specifies whether the tune is PlaySID specific, e.g. uses PlaySID
  samples (psidSpecific):
  0 = C64 compatible,
  1 = PlaySID specific.

This is a v2NG specific field.

PlaySID samples were invented to facilitate playback of C64 volume register
samples with the original Amiga PlaySID software. PlaySID samples made samples
a reality on slow Amiga hardware with a player that was updated only once a
frame.

Unfortunately, converting C64 volume samples to PlaySID samples means that
they can no longer be played on a C64, and furthermore the conversion might
potentially break the non-sample part of a tune if the timing between writes
to the SID registers is at all altered. This follows from the ADSR bugs in the
SID chip.

Today, the speed of common hardware and the sophistication of the SID players
is such that there is little need for PlaySID samples. However, with all the
PlaySID sample PSIDs in existence we need to differentiate between PSID files
containing only original C64 code and PSID files containing PlaySID samples or
having other PlaySID specific issues. As stated above, bit 1 in 'flags' is
reserved for this purpose.

- Bits 2-3 specify the video standard (clock):
  00 = Unknown,
  01 = PAL,
  10 = NTSC,
  11 = PAL and NTSC.

This is a v2NG specific field.

As can be seen from the 'speed' field, it is not possible to specify NTSC C64
playback. This is unfortunate, since the different clock speeds means that a
tune written for the NTSC C64 will be slightly detuned if played back on a PAL
C64. Furthermore, NTSC C64 tunes driven by a vertical blank interrupt have to
be converted to use the CIA 1 timer to fit into this scheme. This can cause
severe problems, as the NTSC refresh rate is once every 17045 cycles, while
the CIA 1 timer A is latched with 17095 cycles. Apart from the difference in
timing itself, the SID ADSR bugs can actually break the tune.

The 'clock' (video standard) field was introduced to circumvent this problem.

- Bits 4-5 specify the SID version (sidModel):
  00 = Unknown,
  01 = MOS6581,
  10 = MOS8580,
  11 = MOS6581 and MOS8580.

This is a v2NG specific field.

The MOS6581 and the MOS8580 have three notable differences. First, combined
waveforms are generally louder on a MOS8580, to the extent that some
combinations that are clearly audible on a MOS8580 are completely silent on a
MOS6581. Second, the internal DC levels in the MOS8580 are so small that
software or hardware tricks must be used to play volume samples. Third, the
MOS8580 analog filter has totally different characteristics from the MOS6581
analog filter.

To ensure that music specifically written for one of the two SID versions can
be played back correctly, we have reserved bits 4-5 in 'flags', as stated
above.

- Bits 6-15 are reserved and should be set to 0.

+78    BYTE startPage (relocStartPage)

This is a v2NG specific field.

This is an 8 bit number. If 'startPage' = 0, the PSID file is clean, i.e. it
does not write outside its data range within the driver ranges. In this case
the largest free memory range can be determined from the start address and the
data length of the PSID binary data. If 'startPage' = 0xff, there is not even
a single free page, and driver relocation is impossible. Otherwise,
'startPage' specifies the start page of the single largest free memory range
within the driver ranges. E.g. if 'startPage' = 0x1e, this free memory range
starts at 0x1e00.

+79    BYTE pageLength (relocPages)

This is a v2NG specific field.

This is an 8 bit number. If 'startPage' is not 0, 'pageLength' is set to the
number of free pages starting at 'startPage'. If 'startPage' = 0, 'pageLength'
should be set to 0.

+7A    WORD reserved

This is a 16 bit number and is reserved and should be set to 0.

+7C    <data>

Version 2 of the PSID header ends here. This offset is the start of the binary
C64 data. See also 'loadAddress' for what the first 2 bytes of 'data' might
indicate.


